using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;

public class GameManager : MonoBehaviour {

	static bool helpShown = false;

	static GameManager _gm;
	public static GameManager Instance {
		get {
			if(_gm==null)
				_gm = GameObject.FindObjectOfType<GameManager>();

			return _gm;
		}
	}

	public GameObject playerPrefab;
	Transform playerInstance;
	Transform playerBaseInstance;

	int _score;
	public int score {
		get { return _score; }
		set {
			if(value > _score)
				totalScore += value - _score;
			_score = value;
		}
	}

	public int totalScore;
	public Text scoreText;
	public Text totalScoreText;
	public Text waveText;

	public Transform waveAnnouncer;
	public Transform waveCountdown;
	public Transform respawningTimer;

	public Transform spawnTopLeft;
	public Transform spawnTopRight;
	public Transform spawnBotLeft;
	public Transform spawnBotRight;

	public Camera mapCamera;
	public Transform map;

	public GameObject splashDamagePrefab;

	[System.Serializable]
	public class Wave {
		public GameObject spawnForTL;
		public GameObject spawnForTR;
		public GameObject spawnForBL;
		public GameObject spawnForBR;
	}

	List<GameObject> activeSpawners;

	public Wave[] waves;
	public int currWave = 0;

	float waveStartTimer = 5;
	float waveStartLeft = 5;

	public float regenerationRate = 0f; // HPs per second.
	public float maxHPs = 10f;
	public float maxSpeed = 10f;
	public bool turretsFireMissiles = false;
	public float bulletBonusDamagePerc = 1f;
	public float bulletBonusFireDamage = 0;
	public bool chillBullets = false;
	public float slowZoneEffectiveness = 1.0f;
	public float landMineEffectiveness = 1.0f;
	public float firewallEffectiveness = 1.0f;
	public float nukeDamage = 20;

	public Transform gameOverMenu;

	void Start() {
		playerBaseInstance = GameObject.Find ("playerBase").transform;
		score = 100;
		totalScore = 0;
		SpawnPlayer(false);

		if(helpShown == false) {
			OpenHelp();
		}

	}

	public void ExitGame() {
		if(Application.isWebPlayer) {
			Application.OpenURL("http://quill18.com/");
		}
		else {
			Application.Quit();
		}
	}

	GameObject bulletUpgradePrefab = null;

	public void SetPlayerBullet(GameObject bulletPrefab) {
		bulletUpgradePrefab = bulletPrefab;
		GameObject.Find ("Player").GetComponent<ShootsBullets>().bulletPrefab = bulletPrefab;
	}

	public void RestartGame() {
		Application.LoadLevel(Application.loadedLevel);
	}

	void SpawnPlayer(bool lowerScore) {
		GameObject go = (GameObject)Instantiate(playerPrefab, Vector2.zero, Quaternion.identity);
		go.name = "Player";
		playerInstance = go.transform;

		if(bulletUpgradePrefab != null)
			SetPlayerBullet(bulletUpgradePrefab);

		if(lowerScore) {
			score = score/2;
			if(score < 0)
				score = 0;
		}
	}

	float respawnTimer = 0;

	public int totalEnemiesKilled = 0;

	public bool PlayerIsDead() {
		return playerInstance==null ? true : false;
	}

	void GameOver() {
		Pause();
		gameOverMenu.gameObject.SetActive(true);

		string s = "You killed <color=red>"+totalEnemiesKilled+"</color> enemies";
		s += " and reached wave <color=green>"+ (currWave+1) +"</color>, earning a ";
		s += "total of <color=blue>"+ totalScore +"</color> points before your base was destroyed.";

		gameOverMenu.Find ("Text").GetComponent<Text>().text = s;

		Health[] hs = GameObject.FindObjectsOfType<Health>();
		
		foreach(Health h in hs) {
			Destroy(h.gameObject);
		}
		
		BulletBehaviour[] bs = GameObject.FindObjectsOfType<BulletBehaviour>();
		foreach(BulletBehaviour b in bs) {
			Destroy(b.gameObject);
		}

	}

	Text enemyStrengthText = null;

	void Update () {

		totalScoreText.text = "Score: " + totalScore + (cheated ? " (cheated)" : "");
		scoreText.text = "Upgrade Points: " + score;
		waveText.text = "Wave: " + (currWave + 1);

		if (playerBaseInstance==null) {
			GameOver();
		}

		if(Time.timeScale == 0)
			return;

		if(playerInstance == null && playerBaseInstance != null) {
			if(respawnTimer <= 0)
				respawnTimer = 3.1f;

			respawnTimer -= Time.deltaTime;

			respawningTimer.gameObject.SetActive(true);
			respawningTimer.GetComponent<Text>().text = "Respawning in " + respawnTimer.ToString("f0") + "...";
			if(respawnTimer < 3 && respawnTimer+Time.deltaTime >= 3)
				respawningTimer.GetComponent<Animator>().SetTrigger("Shake");
			if(respawnTimer < 2 && respawnTimer+Time.deltaTime >= 2)
				respawningTimer.GetComponent<Animator>().SetTrigger("Shake");
			if(respawnTimer < 1 && respawnTimer+Time.deltaTime >= 1)
				respawningTimer.GetComponent<Animator>().SetTrigger("Shake");


			if(respawnTimer <= 0) {
				respawningTimer.gameObject.SetActive(false);
				SpawnPlayer(true);
			}
		}

		if(waveStartLeft > 0) {
			waveStartLeft -= Time.deltaTime;

			waveCountdown.gameObject.SetActive(true);
			if(enemyStrengthText==null)
				enemyStrengthText = waveCountdown.Find ("Enemy Strength").GetComponent<Text>();
			enemyStrengthText.text = "Enemy Strength is at "+(DifficultyExponential()*100f).ToString("f0")+"%";
			//waveCountdown.GetComponent<RectTransform>().anchoredPosition = Vector2.zero;
			waveCountdown.GetComponent<Text>().text = "Next Wave In "+ waveStartLeft.ToString("f0") +"...";

			if(waveStartLeft <= 0) {
				waveCountdown.gameObject.SetActive(false);

				//waveAnnouncer.GetComponent<RectTransform>().anchoredPosition = Vector2.zero;
				waveAnnouncer.gameObject.SetActive(true);
				waveAnnouncer.GetComponent<Text>().text = "Wave " + (currWave+1);
				waveAnnouncer.GetComponent<Animator>().SetTrigger("WaveOn");

				mapCamera.GetComponent<Animator>().SetTrigger("WaveOn");
				map.GetComponent<Animator>().SetTrigger("WaveOn");

				StartWave();
			}
		}
		else {
			// Check to see if wave has ended
			bool stillActive = false;
			foreach(GameObject go in activeSpawners) {
				if(go != null) {
					stillActive = true;
					break;
				}
			}

			if(!stillActive) {
				waveStartLeft = waveStartTimer;
				currWave++;
			}

		}
	}

	public float DifficultyExponential() {
		return Mathf.Pow(1.1f, GameManager.Instance.currWave);
	}

	public float DifficultyMultiplier() {
		return 1f + (0.05f * GameManager.Instance.currWave);
	}

	int trainingWaves = 3;

	void StartWave() {
		if(playerBaseInstance==null)
			return;

		if(currWave==1) {
			//upgradeMenuPrompter.gameObject.SetActive(true);
		}

		int effectiveWaveNumber = currWave;
		if(currWave >= waves.Length) {
			effectiveWaveNumber = trainingWaves + ( (currWave-trainingWaves) % (waves.Length-trainingWaves)  );
		}

		activeSpawners = new List<GameObject>();

		if(waves[effectiveWaveNumber].spawnForTL != null)
			activeSpawners.Add ( (GameObject)Instantiate(waves[effectiveWaveNumber].spawnForTL, spawnTopLeft.transform.position, spawnTopLeft.transform.rotation) );
		if(waves[effectiveWaveNumber].spawnForTR != null)
			activeSpawners.Add ( (GameObject)Instantiate(waves[effectiveWaveNumber].spawnForTR, spawnTopRight.transform.position, spawnTopRight.transform.rotation) );
		if(waves[effectiveWaveNumber].spawnForBL != null)
			activeSpawners.Add ( (GameObject)Instantiate(waves[effectiveWaveNumber].spawnForBL, spawnBotLeft.transform.position, spawnBotLeft.transform.rotation) );
		if(waves[effectiveWaveNumber].spawnForBR != null)
			activeSpawners.Add ( (GameObject)Instantiate(waves[effectiveWaveNumber].spawnForBR, spawnBotRight.transform.position, spawnBotRight.transform.rotation) );
	}

	public void DoSplashDamage(Vector2 pos, float radius, float damage, bool isBurnDamage, int layerMask) {

		Collider2D[] cols = Physics2D.OverlapCircleAll(pos, radius, layerMask);

		foreach(Collider2D col in cols) {
			Health h = col.GetComponent<Health>();
			if(h != null) {
				// Make sure there's no level geometry between us.
				RaycastHit2D hit = Physics2D.Linecast(pos, col.transform.position, 1 );

				if(hit.collider == null) {
					h.TakeDamage(damage, isBurnDamage);
				}
			}
		}

		GameObject go = (GameObject)Instantiate(splashDamagePrefab, pos, Quaternion.identity);
		go.transform.localScale = Vector3.one * radius;

	}

	public void Pause() {
		Time.timeScale = 0;
	}

	public void Unpause() {
		Time.timeScale = 1;
	}

	public GameObject helpPanel;

	public void OpenHelp() {
		helpPanel.SetActive(true);
		Pause ();
	}
	public void CloseHelp() {
		helpPanel.SetActive(false);
		Unpause ();
	}

	bool cheated = false;

	public void DoCheat() {
		score += 1000;
		cheated = true;
	}
}
